/**
 * Copyright 2013 ABSir's Studio
 * 
 * All right reserved
 *
 * Create on 2013-10-22 下午2:02:53
 */
package com.absir.server.socket;

import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.absir.context.core.ContextUtils;
import com.absir.server.exception.ServerException;
import com.absir.server.exception.ServerStatus;
import com.absir.server.socket.resolver.SocketChannelResolver;

/**
 * @author absir
 * 
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class SocketServer {

	/** LOGGER */
	protected static final Logger LOGGER = LoggerFactory.getLogger(SocketServer.class);

	/** serverSocket */
	protected ServerSocketChannel serverSocketChannel;

	/** serverSelector */
	protected Selector serverSelector;

	/** port */
	protected int port;

	/** ip */
	protected String ip;

	/** ipV6 */
	protected String ipV6;

	/**
	 * @return the serverSocketChannel
	 */
	public ServerSocketChannel getServerSocketChannel() {
		return serverSocketChannel;
	}

	/**
	 * @return the port
	 */
	public int getPort() {
		return port;
	}

	/**
	 * 开始服务
	 * 
	 * @param port
	 * @param backlog
	 * @param inetAddress
	 * @param bufferSize
	 * @param receiveBufferSize
	 * @param sendBufferSize
	 * @param channelResolver
	 * @param receiver
	 * @throws IOException
	 */
	public synchronized void start(int port, int backlog, InetAddress inetAddress, final int bufferSize, int receiveBufferSize,
			final int sendBufferSize, final SocketChannelResolver channelResolver, final SocketReceiver receiver)
			throws IOException {
		if (serverSocketChannel != null) {
			throw new ServerException(ServerStatus.ON_FAIL);
		}

		LOGGER.info("start socket port " + port);
		// 初始化监听服务
		serverSocketChannel = ServerSocketChannel.open();
		serverSocketChannel.configureBlocking(false);
		serverSocketChannel.socket().setReceiveBufferSize(receiveBufferSize);
		serverSocketChannel.socket().bind(new InetSocketAddress(inetAddress, port), backlog);

		// 接受请求
		serverSelector = Selector.open();
		serverSocketChannel.register(serverSelector, SelectionKey.OP_ACCEPT);
		ContextUtils.getThreadPoolExecutor().execute(new Runnable() {

			@Override
			public void run() {
				Selector selector = serverSelector;
				ByteBuffer buffer = ByteBuffer.allocate(bufferSize);
				while (selector == serverSelector && !Thread.interrupted()) {
					try {
						selector.select();
						Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
						while (iterator.hasNext()) {
							SelectionKey key = iterator.next();
							iterator.remove();
							if (key.isAcceptable()) {
								// 接受请求
								SocketChannel socketChannel = ((ServerSocketChannel) key.channel()).accept();
								socketChannel.configureBlocking(false);
								socketChannel.socket().setSendBufferSize(sendBufferSize);
								try {
									// 处理注册请求
									if (receiver.accept(socketChannel)) {
										socketChannel.register(selector, SelectionKey.OP_READ);
										continue;
									}

								} catch (Throwable e) {
								}

								close(socketChannel);

							} else {
								// 处理数据
								SocketBuffer socketBuffer = (SocketBuffer) key.attachment();
								SocketChannel socketChannel = (SocketChannel) key.channel();
								buffer.clear();
								try {
									int length = socketChannel.read(buffer);
									if (length > 0) {
										if (socketBuffer == null) {
											socketBuffer = channelResolver.createSocketBuff();
											key.attach(socketBuffer);
										}

										byte[] array = buffer.array();
										int position = 0;
										while (position < length) {
											position = channelResolver.readByteBuffer(socketBuffer, array, position, length);
											if (socketBuffer.getBuff() != null
													&& socketBuffer.getLength() <= socketBuffer.getBuffLengthIndex()) {
												if (socketBuffer.getId() == null) {
													receiver.register(socketChannel, socketBuffer);
													Serializable id = socketBuffer.getId();
													if (id == null) {
														break;
													}

												} else {
													receiver.receiveByteBuffer(socketChannel, socketBuffer);
												}

												channelResolver.readByteBufferDone(socketBuffer);
											}

											position++;
										}

										continue;
									}

								} catch (Throwable e) {
									if (SocketServerContext.getCloseDebug()) {
										e.printStackTrace();
									}
								}

								// 注销请求
								key.cancel();
								try {
									if (socketBuffer != null) {
										Serializable id = socketBuffer.getId();
										if (id != null && id != SocketReceiverContext.UN_REGISTER_ID) {
											receiver.unRegister(socketBuffer.getId(), socketChannel);
										}
									}

								} catch (Throwable e) {
									if (SocketServerContext.getCloseDebug()) {
										e.printStackTrace();
									}
								}

								close(socketChannel);
							}
						}

					} catch (Throwable e) {
						if (SocketServerContext.getCloseDebug()) {
							e.printStackTrace();
						}
					}
				}

				try {
					selector.close();

				} catch (Throwable e) {
				}

				receiver.clearAll();
				close();
			}

		});
	}

	/**
	 * 关闭服务
	 */
	public synchronized void close() {
		if (serverSocketChannel != null) {
			try {
				serverSocketChannel.close();
				LOGGER.info("stop socket port " + serverSocketChannel.socket().getLocalPort());

			} catch (Throwable e) {
			}

			serverSocketChannel = null;
			serverSelector = null;
		}
	}

	/**
	 * @param socketChannel
	 */
	public static void close(SocketChannel socketChannel) {
		try {
			socketChannel.close();

		} catch (Throwable e) {
		}

		if (SocketServerContext.getCloseDebug()) {
			SocketServerContext.getLogger().error("close " + socketChannel, new Exception());
		}
	}
}
